<script setup>
import { parseColor, hsv2rgb, rgb2hsv, rgba2hex, hex2rgba } from '@/utils/color'
import { nextTick } from 'vue';
const props = defineProps({
  modelValue: {
    type: [Object, String],
    default: () => {
      return {
        r: 217,
        g: 128,
        b: 95,
        a: 1
      }
    }
  },
  position: {
    type: String,
    default: 'top',
    validator(val) {
      return ['top', 'tl', 'tr', 'bottom', 'bl', 'br', 'left', 'lt', 'lb', 'right', 'rt', 'rb'].includes(val)
    }
  },
  disabled: {
    type: Boolean,
    default: false
  },
  preset: {
    type: Array,
    default: () => {
      return [
        "#D0021B",
        "#F5A623",
        "#F8E71C",
        "#8B572A",
        "#7ED321",
        "#417505",
        "#BD10E0",
        "#9013FE",
        "#4A90E2",
        "#50E3C2",
        "#B8E986",
        "#000000",
        "#4A4A4A",
        "#9B9B9B",
        "#FFFFFF",
      ]
    }
  },
  size: {
    type: String,//'mini' | 'small' | 'medium' | 'large'
    default: 'medium'
  }
})

const emits = defineEmits(['update:modelValue'])

const saturation_value = ref(null)
const hue_slider = ref(null)
const alpha_slider = ref(null)

const pointStyle = ref("top: 25%;left: 80%;");
const hueSliderStyle = ref("left: 0;");
const alphaSliderStyle = ref("left: calc(100% - 6px);");

const hue = ref(0);
const saturation = ref(1);
const value = ref(1)

const red = ref(255);
const green = ref(0);
const blue = ref(0);

const alpha = ref(1);

const disabled = ref(props.disabled);

onMounted(() => {
  const { r, g, b, a } = parseColor(props.modelValue);
  red.value = r;
  green.value = g;
  blue.value = b;
  alpha.value = a;
});

watch([red, green, blue, alpha], (newValue) => {
  emits("update:modelValue", {
    r: red.value,
    g: green.value,
    b: blue.value,
    a: alpha.value,
  });

  let { h, s, v } = rgb2hsv(red.value, green.value, blue.value);

  hue.value = h;
  saturation.value = s;
  value.value = v;

  // 移动背景板圆圈
  pointStyle.value = `top: ${100 - v * 100}%;left: ${s * 100}%;`;
  // 移动色调滑块
  hueSliderStyle.value = `left: ${(hue.value / 360) * 100}%;`;
});

const colorObj = computed(() => {
  const r = red.value;
  const g = green.value;
  const b = blue.value;
  const a = alpha.value;
  const h = hue.value;
  const s = saturation.value;
  const v = value.value;
  return {
    rgb: `rgba(${r},${g},${b})`,
    rgba: `rgba(${r},${g},${b},${a})`,
    hex6: rgba2hex(r, g, b),
    hex8: rgba2hex(r, g, b, a),
    hsv: `hsv(${h},${s},${v})`,
    hsl: ``,
  };
});

const hexChange = (e) => {
  const v = e.target.value;
  if (/^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(v)) {
    const { r, g, b, a } = hex2rgba(v);
    red.value = r;
    green.value = g;
    blue.value = b;
    alpha.value = a;
  }
}

const redChange = (e) => {
  const v = e.target.value;
  if (v !== "") {
    v > 255 && (red.value = 255);
    v < 0 && (red.value = 0);
    v >= 0 && v <= 255 && (red.value = parseInt(v));
  }
}

const greenChange = (e) => {
  const v = e.target.value;
  if (v !== "") {
    v > 255 && (green.value = 255);
    v < 0 && (green.value = 0);
    v >= 0 && v <= 255 && (green.value = parseInt(v));
  }
}

const blueChange = (e) => {
  const v = e.target.value;
  if (v !== "") {
    v > 255 && (blue.value = 255);
    v < 0 && (blue.value = 0);
    v >= 0 && v <= 255 && (blue.value = parseInt(v));
  }
}

const alphaChange = (e) => {
  const v = e.target.value;
  if (v !== "") {
    v = parseFloat(v);
    alpha.value = v;
    v > 1 && (alpha.value = 1);
    v < 0 && (alpha.value = 0);
    v >= 0 && v <= 1 && (alpha.value = v);
  }
}

// 点击预设方块事件
const presetChange = (item) => {
  if (/^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(item)) {
    const { r, g, b, a } = hex2rgba(item);
    red.value = r;
    green.value = g;
    blue.value = b;
    alpha.value = a;
  }
}

const handleChangeSV = (e) => {
  let w = saturation_value.value.clientWidth;
  let h = saturation_value.value.clientHeight;
  let x = e.pageX - saturation_value.value.getBoundingClientRect().left;
  let y = e.pageY - saturation_value.value.getBoundingClientRect().top;
  x = x < w && x > 0 ? x : x > w ? w : 0;
  y = y < h && y > 0 ? y : y > h ? h : 0;
  // 计算饱和度和亮度
  saturation.value = Math.floor((x / w) * 100 + 0.5) / 100;
  value.value = Math.floor((1 - y / h) * 100 + 0.5) / 100;
  // hsv转化为rgb
  let { r, g, b } = hsv2rgb(hue.value, saturation.value, value.value);
  red.value = r;
  green.value = g;
  blue.value = b;
  // 移动背景板圆圈
  pointStyle.value = `top: ${y}px;left: ${x}px;`;
}

const mousedownSV = (e) => {
  // 鼠标按下计算饱和度和亮度并添加事件
  handleChangeSV(e);
  // 添加整个页面的鼠标事件
  window.addEventListener("mousemove", handleChangeSV);
  window.addEventListener("mouseup", mouseupSV);
}

const mouseupSV = (e) => {
  // 鼠标松开后移除事件
  window.removeEventListener("mousemove", handleChangeSV);
  window.removeEventListener("mouseup", mouseupSV);
}

// 色调
const handleChangeHue = (e) => {
  let w = hue_slider.value.clientWidth;
  let x =
    e.pageX - saturation_value.value.getBoundingClientRect().left;
  x = x < w && x > 0 ? x : x > w ? w : 0;
  // 计算色调
  hue.value = Math.floor((x / w) * 360 + 0.5);
  // hsv转化为rgb
  let { r, g, b } = hsv2rgb(hue.value, saturation.value, value.value);
  red.value = r;
  green.value = g;
  blue.value = b;
  // 移动滑块
  hueSliderStyle.value = `left: ${x >= w - 6 ? w - 6 : x}px;`;
}

const mousedownHue = (e) => {
  handleChangeHue(e);
  window.addEventListener("mousemove", handleChangeHue);
  window.addEventListener("mouseup", mouseupHue);
}

const mouseupHue = (e) => {
  window.removeEventListener("mousemove", handleChangeHue);
  window.removeEventListener("mouseup", mouseupHue);
}

const handleChangeAlpha = (e) => {
  let w = alpha_slider.value.clientWidth;
  let x =
    e.pageX - saturation_value.value.getBoundingClientRect().left;
  x = x < w && x > 0 ? x : x > w ? w : 0;
  // 计算透明度
  alpha.value = Math.floor((x / w) * 100 + 0.5) / 100;
  // 移动滑块
  alphaSliderStyle.value = `left: ${x >= w - 6 ? w - 6 : x}px;`;
}

const mousedownAlpha = (e) => {
  handleChangeAlpha(e);
  window.addEventListener("mousemove", handleChangeAlpha);
  window.addEventListener("mouseup", mouseupAlpha);

}

const mouseupAlpha = (e) => {
  window.removeEventListener("mousemove", handleChangeAlpha);
  window.removeEventListener("mouseup", mouseupAlpha);
}

watch(() => props.disabled, (val) => {
  disabled.value = val
})

</script>

<template>
  <div :class="['color-picker-box', props.size, disabled ? 'disabled' : null]" v-if="disabled">
    <div class="color-picker" :style="`background-color: ${colorObj.rgba};`"></div>
  </div>
  <a-popover trigger="click" v-else :position="props.position" :arrow-style="{ display: '_none' }">
    <div :class="['color-picker-box', props.size]">
      <div class="color-picker" :style="`background-color: ${colorObj.rgba};`"></div>
    </div>
    <template #content>
      <div class="color-select">
        <div class="saturation-value" ref="saturation_value" @mousedown="mousedownSV">
          <div :style="`background-color: hsl(${hue}, 100%, 50%);`">
            <div class="point" :style="pointStyle"></div>
          </div>
          <div class="saturation-value-2"></div>
          <div class="saturation-value-3"></div>
        </div>
        <div class="color-select-middle">
          <div style="flex: auto">
            <div class="hue-slider" ref="hue_slider" @mousedown="mousedownHue">
              <div class="slider" :style="hueSliderStyle"></div>
            </div>
            <div class="alpha-slider" ref="alpha_slider" @mousedown="mousedownAlpha">
              <div class="slider" :style="alphaSliderStyle"></div>
              <div
                :style="`background: linear-gradient(to right, rgba(0,0,0,0), ${colorObj.rgb});width: 100%;height: 100%`">
              </div>
            </div>
          </div>
          <div class="color-diamond">
            <div
              :style="`background-color: ${colorObj.rgba};width: 100%;height: 100%;box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15), inset 0 0 4px rgba(0, 0, 0, .25);`">
            </div>
          </div>
        </div>
        <div class="color-value">
          <div class="hex">
            <label>
              <input :value="colorObj.hex8" @input="hexChange" spellcheck="false" />
            </label>
            <p>Hex</p>
          </div>
          <div class="rgba-r">
            <label>
              <input :value="red" @input="redChange" />
            </label>
            <p>R</p>
          </div>
          <div class="rgba-g">
            <label>
              <input :value="green" @input="greenChange" />
            </label>
            <p>G</p>
          </div>
          <div class="rgba-b">
            <label>
              <input :value="blue" @input="blueChange" />
            </label>
            <p>B</p>
          </div>
          <div class="rgba-a">
            <label>
              <input :value="alpha" @input="alphaChange" />
            </label>
            <p>A</p>
          </div>
        </div>
        <ul class="preset">
          <li v-for="item in preset" :key="item" :style="`background-color: ${item}`" @click="presetChange(item)"></li>
        </ul>
      </div>
    </template>
  </a-popover>
</template>

<style lang="less" scoped>
@import '@arco-design/web-vue/es/style/theme/global.less';

.color-picker-box {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 2px;
  border: 1px solid var(--color-neutral-3);
  border-radius: var(--border-radius-medium);
  cursor: pointer;

  &.mini {
    width: @size-6;
    height: @size-6;
  }

  &.small {
    width: @size-7;
    height: @size-7;
  }

  &.medium {
    width: @size-8;
    height: @size-8;
  }

  &.large {
    width: @size-9;
    height: @size-9;
  }

  &.disabled {
    cursor: not-allowed;
  }

  .color-picker {
    width: calc(100% - 4px);
    height: calc(100% - 4px);
    border: 1px solid var(--color-neutral-4);
  }
}

.color-select {
  position: relative;
  user-select: none;
  width: 100%;
  background: var(--color-bg-5);
  padding: 10px;
  /*border: 1px solid #ccc;*/
  /*border-radius: 10px;*/

  /* 饱和度和亮度 */
  .saturation-value {
    cursor: pointer;
    width: 100%;
    height: 200px;
    position: relative;
    margin-bottom: 10px;
    box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);

    &>div {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }

    /* 圆圈 */
    .point {
      box-sizing: border-box;
      width: 6px;
      height: 6px;
      background-color: transparent;
      border: 2px solid #ccc;
      border-radius: 50%;
      transform: translate(-50%, -50%);
      position: absolute;
      z-index: 9;
    }

    .saturation-value-2 {
      background: linear-gradient(to right, white, #ffffff00);
    }

    .saturation-value-3 {
      background: linear-gradient(to top, black, #ffffff00);
    }
  }

  /* 色调 透明度 */
  .color-select-middle {
    width: 100%;
    display: flex;
    margin-bottom: 10px;

    /* 色调滑块条 */
    .hue-slider {
      position: relative;
      margin-bottom: 6px;
      height: 10px;
      background: linear-gradient(90deg,
          red 0,
          #ff0 17%,
          #0f0 33%,
          #0ff 50%,
          #00f 67%,
          #f0f 83%,
          red);
      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
    }

    /* 透明度滑块条 */
    .alpha-slider {
      position: relative;
      height: 10px;
      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
      background: #fff url("");
      background-size: 10px 10px;
    }

    /* 滑块 */
    .slider {
      position: absolute;
      box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
      box-sizing: border-box;
      width: 6px;
      height: 100%;
      background-color: #fff;
    }
  }

  /* 颜色的值 hex rgba */
  .color-value {
    width: 100%;
    display: flex;
    justify-content: space-between;

    div {
      padding: 0 3px;
      text-align: center;
    }

    input {
      font-size: 12px;
      box-sizing: border-box;
      width: 34px;
      height: 24px;
      padding: 0;
      margin: 0;
      outline: none;
      text-align: center;
      border-radius: var(--border-radius-small);
      background-color: var(--color-fill-2);
      border: 1px solid var(--color-neutral-3);
      color: var(--color-text-1);

      &:hover{
        background-color: var(--color-fill-3);
      }
      &:focus{
        border-color: rgb(var(--primary-6));
      }
    }

    p {
      font-size: 12px;
      margin: 3px 0 0;
    }

    .rgba-a {
      padding-right: 0;
    }

    .hex {
      flex: 1;
      padding-left: 0;

      input {
        width: 100%;
        height: 24px;
      }
    }
  }

  /* 预设颜色  */
  .preset {
    width: 100%;
    padding: 0;
    margin: 10px 0 0;
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;

    li {
      width: 20px;
      height: 20px;
      margin-right: 6px;
      margin-bottom: 6px;
      border: 1px solid var(--color-neutral-3);
      border-radius: var(--border-radius-medium);
    }
  }
}
</style>